home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / ip / ppp / mac / macppp1.1.3-src.hqx / MacPPP1.1.3-src / ppp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-15  |  19.2 KB  |  729 lines

  1. /*
  2.  *  ppp.c - ppp support routines
  3.  *
  4.  * Copyright 1991-1992 William Allen Simpson
  5.  *  Licensed for non-profit non-commercial distribution
  6.  *  in Merit's PPP LAP for MacTCP
  7.  *
  8.  * Copyright 1992-1993 Merit Network, Inc. and The Regents of the
  9.  *  University of Michigan.  Usage of this source code is restricted
  10.  *  to non-profit, non-commercial purposes.  The source is provided
  11.  *  "as-is", without warranty.
  12.  *
  13.  * This code has been derived solely from the Jan 1991 public
  14.  *  domain release of PPP included in Phil Karn's KA9Q.
  15.  */
  16. #include "ppp.h"
  17.  
  18. /* Convert PPP header in host form to network form */
  19. void
  20. htonppp(LapInfo *lap, register b_16 protocol, struct bufheader *bufptr)
  21. {
  22.     struct proto_s *proto_p = &(lap->lcp_i);
  23.     register char *cp;
  24.     register long len;
  25.     Boolean full_lcp, full_prot, full_acf;
  26.  
  27.     full_lcp = ( protocol == PPP_LCP_PROTOCOL );
  28.     full_prot = ( full_lcp
  29.         || !(proto_p->remote.work_negotiate & LCP_N_PFC)
  30.         || protocol >= 0x00FF );
  31.     full_acf = ( full_lcp
  32.         || !(proto_p->remote.work_negotiate & LCP_N_ACFC) );
  33.  
  34.     len = full_acf * 2 + full_prot + 1;
  35.  
  36.     /* Prepend header onto packet data */
  37.     bufptr->length += len;
  38.     cp = (bufptr->dataptr -= len);
  39.     
  40.     /* Load header with proper values */
  41.     if ( full_acf ) {
  42.         *cp++ = HDLC_ALL_ADDR;
  43.         *cp++ = HDLC_UI;
  44.     }
  45.     if ( full_prot )
  46.         *cp++ = protocol >> 8;
  47.  
  48.     *cp++ = protocol;
  49. }
  50.  
  51. /* Check for PPP Network-Layer Protocol Phase */
  52. void ppp_ready(LapInfo *lap)
  53. {
  54.     if ( !(lap->ppp_flags & PPP_AP_REMOTE) ) {
  55.         /* no pending authentication */
  56.         lap->ppp_phase = pppNETWORK;
  57.         lap->ppp_upsince = TickCount();
  58.         fsm_up( &(lap->ppp_fsm[IPcp]) );        /* send an UP event to IPcp */
  59.     }
  60. }
  61.  
  62. void
  63. ppp_init (register LapInfo *lap )
  64. {
  65.     register struct fsm_s *fsm_p;
  66.  
  67.     lap->ppp_phase = pppDEAD;
  68.  
  69.     /* init LCP */
  70.     fsm_p = &(lap->ppp_fsm[Lcp]);
  71.     fsm_p->pdc.protocol = PPP_LCP_PROTOCOL;
  72.     fsm_p->pdc.option_limit = LCP_OPTION_LIMIT;
  73.     fsm_p->pdc.option_lengths = lap->lcp_option_length_p;
  74.     fsm_p->pdc.fsmi = Lcp;
  75.     fsm_p->pdc.try_nak = LCP_NAK_TRY;
  76.     fsm_p->pdc.try_terminate = LCP_TERM_TRY;
  77.     fsm_p->lap = lap;
  78.     fsm_p->pdv = &(lap->lcp_i);
  79.     fsm_init(fsm_p);
  80.  
  81.     /* init IPCP */
  82.     fsm_p = &(lap->ppp_fsm[IPcp]);
  83.     fsm_p->pdc.protocol = PPP_IPCP_PROTOCOL;
  84.     fsm_p->pdc.fsmi = IPcp;
  85.     fsm_p->pdc.try_nak = IPCP_NAK_TRY;
  86.     fsm_p->pdc.try_terminate = IPCP_TERM_TRY;
  87.     fsm_p->pdc.option_limit = IPCP_OPTION_LIMIT;
  88.     fsm_p->pdc.option_lengths = lap->ipcp_option_length_p;
  89.     fsm_p->lap = lap;
  90.     fsm_p->pdv = &(lap->ipcp_i);
  91.     fsm_init(fsm_p);
  92.  
  93.     /* init PAP */
  94.     fsm_p = &(lap->ppp_fsm[Pap]);
  95.     fsm_p->pdc.protocol = PPP_PAP_PROTOCOL;
  96.     fsm_p->pdc.fsmi = Pap;
  97.     fsm_p->lap = lap;
  98.     fsm_p->pdv = &(lap->pap_i);
  99.     fsm_p->state = fsmCLOSED;
  100. }
  101.  
  102. /* Keep track of changes in I-O status */
  103. int
  104. ppp_iostatus(LapInfo *lap, int command)
  105. {
  106.     switch ( command ) {
  107.     case PARAM_UP:
  108.         if ( lap->ppp_phase == pppDEAD ) {
  109.             lap->ppp_phase = pppESTABLISH;
  110.         }
  111.         fsm_up( &(lap->ppp_fsm[Lcp]) );
  112.         return 0;
  113.  
  114.     case PARAM_DOWN:
  115.         fsm_down( &(lap->ppp_fsm[Lcp]) );
  116.         lap->ppp_phase = pppDEAD;
  117.         return 0;
  118.     };
  119.     return -1;
  120. }
  121.  
  122. /* Process configuration ACK sent by remote host */
  123. int proc_ack(fsm_p, config, bufptr)
  124. struct fsm_s *fsm_p;
  125. struct config_hdr *config;
  126. struct bufheader *bufptr;
  127. {
  128.     struct bufheader *req_buf;
  129.  
  130.     /* ID field must match last request we sent */
  131.     if (config->id != fsm_p->lastid) {
  132.         PPP_DEBUG_CHECKS("\pACK: wrong ID");
  133.         goto bad_return;
  134.     }
  135.  
  136.     /* Get a copy of last request we sent */
  137.     if ( (req_buf = makereq(fsm_p)) == nil)
  138.         goto bad_return;
  139.  
  140.     /* Overall buffer length should match */
  141.     if (config->len != req_buf->length) {
  142.         PPP_DEBUG_CHECKS("\pACK: buffer length mismatch");
  143.             goto bad_return2;
  144.         } else {
  145.         register int req_char;
  146.         register int ack_char;
  147.  
  148.         /* Each byte should match */
  149.         while ((req_char = yankbyte(req_buf)) != -1) {
  150.             if ((ack_char = yankbyte(bufptr)) == -1
  151.              || ack_char != req_char ) {
  152.                 PPP_DEBUG_CHECKS("\pACK: data mismatch");
  153.                 goto bad_return2;
  154.             }
  155.         }
  156.     }
  157.     release(req_buf);
  158.     release(bufptr);
  159.     return 0;
  160.  
  161. bad_return2:
  162.     release(req_buf);
  163. bad_return:
  164.     release(bufptr);
  165.     return -1;
  166. }
  167.  
  168. /************************************************************************/
  169. /* Process configuration NAK sent by remote host */
  170. int proc_nak(fsm_p, config, bufptr)
  171. struct fsm_s *fsm_p;
  172. struct config_hdr *config;
  173. struct bufheader *bufptr;
  174. {
  175.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  176.     long signed_length = config->len;
  177.     struct option_hdr option;
  178.     int last_option = 0;
  179.     int result;
  180.  
  181.     /* ID field must match last request we sent */
  182.     if (config->id != fsm_p->lastid) {
  183.         PPP_DEBUG_CHECKS("\pNAK: wrong ID");
  184.         goto bad_return;
  185.     }
  186.  
  187.     /* First, process in order.  Then, process extra "important" options */
  188.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  189.         if ((signed_length -= option.len) < 0) {
  190.             PPP_DEBUG_CHECKS("\pNAK: bad header length");
  191.             goto bad_return;
  192.         }
  193.         if ( option.type > fsm_p->pdc.option_limit ) {
  194.             PPP_DEBUG_CHECKS("\pNAK: option out of range");
  195.         } else if ( option.type < last_option
  196.           || !(proto_p->local.work_negotiate & (1 << option.type)) ) {
  197.             if (proto_p->local.work_negotiate & (1 << option.type)) {
  198.                 PPP_DEBUG_CHECKS("\pNAK: option out of order");
  199.                 goto bad_return;
  200.             }
  201.             proto_p->local.work_negotiate |= (1 << option.type);
  202.             last_option = fsm_p->pdc.option_limit + 1;
  203.         } else {
  204.             last_option = option.type;
  205.         }
  206.         if ( ( result = option_check( bufptr, fsm_p, &option, FALSE ) ) == -1 ) {
  207.             PPP_DEBUG_CHECKS("\pNAK: ran out of data");
  208.             goto bad_return;
  209.         }
  210.         /* update the negotiation status */
  211.         if ( result == CONFIG_REJ
  212.           && option.type <= fsm_p->pdc.option_limit ) {
  213.             proto_p->local.work_negotiate &= ~(1 << option.type);
  214.         }
  215.     }
  216.     release(bufptr);
  217.     return 0;
  218.  
  219. bad_return:
  220.     release(bufptr);
  221.     return -1;
  222. }
  223.  
  224. /* Process configuration reject sent by remote host */
  225. int
  226. proc_reject(fsm_p, config, bufptr)
  227. struct fsm_s *fsm_p;
  228. struct config_hdr *config;
  229. struct bufheader *bufptr;
  230. {
  231.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  232.     long signed_length = config->len;
  233.     struct option_hdr option;
  234.     int last_option = 0;
  235.  
  236.     /* ID field must match last request we sent */
  237.     if (config->id != fsm_p->lastid) {
  238.         PPP_DEBUG_CHECKS("\pREJ: wrong ID");
  239.         goto bad_return;
  240.     }
  241.  
  242.     /* Process in order, checking for errors */
  243.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  244.         register int k;
  245.  
  246.         if ((signed_length -= option.len) < 0) {
  247.             PPP_DEBUG_CHECKS("\pREJ: bad header length");
  248.             goto bad_return;
  249.         }
  250.         if ( option.type > fsm_p->pdc.option_limit ) {
  251.             PPP_DEBUG_CHECKS("\pIPCP REJ: option out of range");
  252.         } else if (option.type < last_option
  253.          || !(proto_p->local.work_negotiate & (1 << option.type))) {
  254.             PPP_DEBUG_CHECKS("\pREJ: option out of order");
  255.             goto bad_return;
  256.         }
  257.         for ( k = option.len - OPTION_HDR_LEN; k-- > 0; ) {
  258.             if ( yankbyte(bufptr) == -1 ) {
  259.                 PPP_DEBUG_CHECKS("\pREJ: ran out of data");
  260.                 goto bad_return;
  261.             }
  262.         }
  263.  
  264.         if ( (last_option = option.type) <= fsm_p->pdc.option_limit) {
  265.             proto_p->local.work_negotiate &= ~(1 << option.type);
  266.             if (fsm_p->pdc.fsmi == IPcp && 
  267.                     option.type == IPCP_ADDRESS) { /* try old style address negotiationg */
  268.                 proto_p->local.work_negotiate |= IPCP_N_ADDRESSES;
  269.                 if (proto_p->remote.want_negotiate & IPCP_N_ADDRESS)
  270.                     proto_p->remote.want_negotiate ^=
  271.                         IPCP_N_ADDRESS | IPCP_N_ADDRESSES;
  272.             }
  273.         }
  274.     }
  275.     release(bufptr);
  276.     return 0;
  277.  
  278. bad_return:
  279.     release(bufptr);
  280.     return -1;
  281. }
  282.  
  283. /* Check the options, updating the working values.
  284.  * Returns -1 if ran out of data, ACK/NAK/REJ as appropriate.
  285.  */
  286.  
  287. int option_check( bufptr, fsm_p, option_p, request )
  288. struct bufheader *bufptr;
  289. struct fsm_s *fsm_p;
  290. struct option_hdr *option_p;
  291. int request;
  292. {
  293.     int toss = option_p->len - OPTION_HDR_LEN;
  294.     register int option_result = CONFIG_ACK;        /* Assume good values */
  295.     int test;
  296.     register long temp_val;
  297.     register b_8 optype = option_p->type;
  298.     char *cp = bufptr->dataptr;
  299.     struct proto_s *proto_p = ( (struct proto_s *) (fsm_p->pdv) );
  300.     struct    option_s *side_p;
  301.     
  302.     if (request)
  303.         side_p = &(proto_p->remote);
  304.     else
  305.         side_p = &(proto_p->local);
  306.  
  307.     switch (fsm_p->pdc.fsmi) {
  308.     case Lcp: {
  309.     
  310.         if ( !(side_p->will_negotiate & (1 << optype))) {
  311.             option_result = CONFIG_REJ;        /* if we aren't willing to negotiate */
  312.             break;
  313.         }
  314.  
  315.         switch(optype) {
  316.         case LCP_MRU:
  317.             side_p->work.lcp_option.mru = yank16(bufptr);
  318.             toss -= 2;
  319.  
  320.             /* Check if new value is appropriate */
  321.             if (side_p->work.lcp_option.mru < LCP_MRU_LO) {
  322.                 side_p->work.lcp_option.mru = LCP_MRU_LO;
  323.                 option_result = CONFIG_NAK;
  324.             } else if (side_p->work.lcp_option.mru > LCP_MRU_HI) {
  325.                 side_p->work.lcp_option.mru = LCP_MRU_HI;
  326.                 option_result = CONFIG_NAK;
  327.             }
  328.             if ( (side_p->want_negotiate & LCP_N_MRU) && !request
  329.               && side_p->work.lcp_option.mru > side_p->want.lcp_option.mru ) {
  330.                 side_p->work.lcp_option.mru = side_p->want.lcp_option.mru;
  331.                 option_result = CONFIG_NAK;
  332.             }
  333.             break;
  334.  
  335.         case LCP_ACCM:
  336.             side_p->work.lcp_option.accm = yank32(bufptr);
  337.             toss -= 4;
  338.             /* Remote host may ask to escape more control  */
  339.             /* characters than we require, but must escape */
  340.             /* at least the control chars that we require. */
  341.             if ( (!request || (side_p->want_negotiate & LCP_N_ACCM))
  342.               && side_p->work.lcp_option.accm !=
  343.                    (side_p->work.lcp_option.accm | side_p->want.lcp_option.accm) ) {
  344.                 side_p->work.lcp_option.accm |= side_p->want.lcp_option.accm;
  345.                 option_result = CONFIG_NAK;
  346.             }    
  347.             break;
  348.  
  349.         case LCP_AUTHENT:
  350.             side_p->work.lcp_option.authentication = yank16(bufptr);
  351.             toss -= 2;
  352.  
  353.             /* Check if new value is appropriate */
  354.             switch ( side_p->work.lcp_option.authentication ) {
  355.             case PPP_PAP_PROTOCOL:
  356.             /* Yes */
  357.                 break;
  358.             default:
  359.                 option_p->len = 4;        /* length of PAP option */
  360.                 side_p->work.lcp_option.authentication = PPP_PAP_PROTOCOL;
  361.                 option_result = CONFIG_NAK;
  362.                 break;
  363.             };
  364.             break;
  365.  
  366.         case LCP_MAGIC:
  367.             side_p->work.lcp_option.magic_number = yank32(bufptr);
  368.             toss -= 4;
  369.  
  370.             /* Ensure that magic numbers are different */
  371.             if (side_p->work.lcp_option.magic_number == 0L
  372.              || proto_p->remote.work.lcp_option.magic_number == \
  373.                  proto_p->local.work.lcp_option.magic_number) {
  374.                 side_p->work.lcp_option.magic_number += TickCount() * (long) bufptr;
  375.                 option_result = CONFIG_NAK;
  376.             }
  377.             break;
  378.  
  379.         case LCP_PFC:
  380.             break;
  381.  
  382.         case LCP_ACFC:
  383.             break;
  384.  
  385.         default:
  386.             option_result = CONFIG_REJ;
  387.         }
  388.       }
  389.           break;
  390.       case IPcp: {
  391.  
  392.         if ( !(side_p->will_negotiate & (1 << optype))) {
  393.             option_result = CONFIG_REJ;        /* if we aren't willing to negotiate */
  394.             break;
  395.         }
  396.  
  397.         switch(optype) {
  398.         case IPCP_ADDRESSES:
  399.         case IPCP_ADDRESS:
  400.             side_p->work.ipcp_option.address = get32(cp);    /* get address */
  401.             if (optype == IPCP_ADDRESSES)
  402.                 side_p->work.ipcp_option.other = get32(cp+4);    /* if addresses, get other address */
  403.             if ( !request ) {
  404.                 /* override any undesirable changes */
  405.                 if (proto_p->remote.want.ipcp_option.address != 0L) {
  406.                     proto_p->local.work.ipcp_option.other
  407.                         = proto_p->remote.want.ipcp_option.address;
  408.                 }
  409.  
  410.                 if (proto_p->local.want.ipcp_option.address != 0L) {
  411.                     proto_p->local.work.ipcp_option.address
  412.                         = proto_p->local.want.ipcp_option.address;
  413.                 }
  414.                 break;
  415.             }
  416.  
  417.             /* Ensure that addresses match */
  418.             if (proto_p->remote.work.ipcp_option.address ==
  419.                     proto_p->remote.want.ipcp_option.address) {
  420.                 if (proto_p->remote.want.ipcp_option.address == 0L) {
  421.                     /* don't know address either */
  422.                     option_result = CONFIG_REJ;
  423.                 }
  424.             } else if (proto_p->remote.want.ipcp_option.address == 0L) {
  425.                 proto_p->local.work.ipcp_option.other =
  426.                         proto_p->remote.work.ipcp_option.address;
  427.             } else {
  428.                 proto_p->remote.work.ipcp_option.address =
  429.                     proto_p->remote.want.ipcp_option.address;
  430.                 option_result = CONFIG_NAK;
  431.             }
  432.  
  433.             if (optype == IPCP_ADDRESSES) {
  434.                 if (proto_p->remote.work.ipcp_option.other ==
  435.                         proto_p->local.want.ipcp_option.address) {
  436.                     if (proto_p->local.want.ipcp_option.address == 0L) {
  437.                         /* don't know address either */
  438.                         option_result = CONFIG_REJ;
  439.                     }
  440.                 } else if (proto_p->local.want.ipcp_option.address == 0L) {
  441.                     proto_p->local.work.ipcp_option.address =
  442.                             proto_p->remote.work.ipcp_option.other;
  443.                 } else {
  444.                     option_result = CONFIG_NAK;
  445.                     proto_p->remote.work.ipcp_option.other =
  446.                         proto_p->local.want.ipcp_option.address;
  447.                 }
  448.             }
  449.             break;
  450.  
  451.         case IPCP_COMPRESS:
  452.             side_p->work.ipcp_option.compression = yank16(bufptr);
  453.             toss -= 2;
  454.             /* Check if requested type is acceptable */
  455.             switch ( side_p->work.ipcp_option.compression ) {
  456.             case PPP_COMPR_PROTOCOL:
  457.                 if ( toss == 0 ) { /* check if old-style VJ option */
  458.                     option_result = CONFIG_REJ;
  459.                     break;
  460.                 }
  461.                 if ( (test = yankbyte(bufptr)) == -1 )
  462.                     return -1;
  463.                 if ( (side_p->work.ipcp_option.slots = test + 1) < IPCP_SLOT_LO) {
  464.                     side_p->work.ipcp_option.slots = IPCP_SLOT_LO;
  465.                     option_result = CONFIG_NAK;
  466.                 } else if (side_p->work.ipcp_option.slots > IPCP_SLOT_HI && !request) {
  467.                     side_p->work.ipcp_option.slots = IPCP_SLOT_HI;
  468.                     option_result = CONFIG_NAK;
  469.                 }
  470.  
  471.                 if ( (test = yankbyte(bufptr)) == -1 )
  472.                     return -1;
  473.                 if ( (side_p->work.ipcp_option.slot_compress = test) > 1 ) {
  474.                     side_p->work.ipcp_option.slot_compress = 1;
  475.                     option_result = CONFIG_NAK;
  476.                 }
  477.                 toss -= 2;
  478.                 break;
  479.  
  480.             default:
  481.                 if ( side_p->want_negotiate & IPCP_N_COMPRESS ) {
  482.                     side_p->work.ipcp_option.compression = side_p->want.ipcp_option.compression;
  483.                     side_p->work.ipcp_option.slots = side_p->want.ipcp_option.slots;
  484.                     side_p->work.ipcp_option.slot_compress = side_p->want.ipcp_option.slot_compress;
  485.                 } else {
  486.                     side_p->work.ipcp_option.compression = PPP_COMPR_PROTOCOL;
  487.                     side_p->work.ipcp_option.slots = IPCP_SLOT_DEFAULT;
  488.                     side_p->work.ipcp_option.slot_compress = IPCP_SLOT_COMPRESS;
  489.                 }
  490.                 option_result = CONFIG_NAK;
  491.                 break;
  492.             };
  493.             break;
  494.  
  495.         default:
  496.             option_result = CONFIG_REJ;
  497.             break;
  498.         }
  499.      }
  500.      }
  501.     if ( toss < 0 )
  502.         return -1;
  503.  
  504.     if ( !request || (option_result != CONFIG_REJ)) {
  505.         /* toss extra bytes in option */
  506.         while( toss-- > 0 ) {
  507.             if ( yankbyte(bufptr) == -1 )
  508.                 return -1;
  509.         }
  510.     }
  511.     return (option_result);
  512. }
  513.  
  514. /* Check options requested by the remote host */
  515. int proc_request(fsm_p, config, bufptr)
  516. struct fsm_s *fsm_p;
  517. struct config_hdr *config;
  518. struct bufheader *bufptr;
  519. {
  520.     long signed_length = config->len;
  521.     struct bufheader *reply_buf;    /* reply packet */
  522.     int reply_result = CONFIG_ACK;        /* reply to request */
  523.     b_16 desired;                /* desired to negotiate */
  524.     struct option_hdr option;        /* option header storage */
  525.     int option_result;            /* option reply */
  526.     struct bufheader *rejdata;        /* for CONFIG rejects to return data */
  527.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  528.  
  529.     proto_p->remote.work_negotiate = FALSE;    /* clear flags */
  530.  
  531.     if ((reply_buf = getbuffer()) == nil)
  532.         goto bad_return2;
  533.  
  534.     /* Process options requested by remote host */
  535.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  536.         if ((signed_length -= option.len) < 0) {
  537.             PPP_DEBUG_CHECKS("\pREQ: bad header length");
  538.             goto bad_return;
  539.         }
  540.  
  541.         if ( ( option_result = option_check(bufptr, fsm_p,
  542.                  &option, TRUE ) ) == -1 ) {
  543.             PPP_DEBUG_CHECKS("\pREQ: ran out of data");
  544.             goto bad_return;
  545.         }
  546.  
  547.         if ( option_result < reply_result ) {
  548.             continue;
  549.         } else if ( option_result > reply_result ) {
  550.             /* Discard current list of replies */
  551.             release(reply_buf);
  552.             reply_buf = getbuffer();
  553.             reply_result = option_result;
  554.         }
  555.  
  556.         /* remember that we processed option */
  557.         if ( option_result != CONFIG_REJ ) {
  558.             proto_p->remote.work_negotiate |= (1 << option.type);
  559.             rejdata = nil;
  560.         } else {
  561.             rejdata = bufptr;
  562.         }
  563.  
  564.         /* Add option response to the return list */
  565.         add_option(fsm_p, reply_buf, &(proto_p->remote.work), option.type, option.len, rejdata);
  566.     }
  567.  
  568.     /* Now check for any missing options which are desired */
  569.     if ( fsm_p->retry_nak > 0
  570.      &&  (desired = proto_p->remote.want_negotiate &
  571.              ~proto_p->remote.work_negotiate) != 0 ) {
  572.         switch ( reply_result ) {
  573.         case CONFIG_ACK:
  574.             release(reply_buf);
  575.             reply_buf = getbuffer();
  576.             reply_result = CONFIG_NAK;
  577.             /* fallthru */
  578.         case CONFIG_NAK:
  579.             makeoptions(fsm_p, reply_buf, &(proto_p->remote.want), desired );
  580.             fsm_p->retry_nak--;
  581.             break;
  582.         case CONFIG_REJ:
  583.             /* do nothing */
  584.             break;
  585.         };
  586.     } else if ( reply_result == CONFIG_NAK ) {
  587.         /* if too many NAKs, reject instead */
  588.         if ( fsm_p->retry_nak > 0 )
  589.             fsm_p->retry_nak--;
  590.         else
  591.             reply_result = CONFIG_REJ;
  592.     }
  593.  
  594.     /* Send ACK/NAK/REJ to remote host */
  595.     fsm_send(fsm_p, reply_result, config->id, reply_buf);
  596.     release(bufptr);
  597.     return (reply_result != CONFIG_ACK);
  598.  
  599. bad_return:
  600.     release(reply_buf);
  601. bad_return2:
  602.     release(bufptr);
  603.     return -1;
  604. }
  605.  
  606. /* Build a list of options */
  607. void makeoptions(struct fsm_s *fsm_p, struct bufheader *bufptr,
  608.                         union value_s *value_p, b_16 negotiating)
  609. {
  610.     register int o_type;
  611.  
  612.     for ( o_type = 1; o_type <= fsm_p->pdc.option_limit ; o_type++ ) {
  613.         if (negotiating & (1 << o_type)) {
  614.             add_option(fsm_p, bufptr, value_p, o_type,
  615.                          *(fsm_p->pdc.option_lengths + o_type), nil);
  616.         }
  617.     }
  618. }
  619.  
  620. /* add an option to a packet */
  621. void add_option(struct fsm_s *fsm_p, struct bufheader *bufptr,
  622.     union value_s *value_p, b_8 o_type, b_8 o_length, struct bufheader *copy_buffer)
  623. {
  624.     register char *cp;
  625.     register int copylen;
  626.     
  627.     cp = bufptr->dataptr + bufptr->length;
  628.     *cp++ = o_type;
  629.     *cp++ = o_length;
  630.     bufptr->length += o_length;
  631.  
  632.     if (copy_buffer != nil) {    /* if we should just copy the data */
  633.         copylen = o_length - OPTION_HDR_LEN;
  634.         while ( copylen-- > 0 ) {
  635.             *cp++ = yankbyte(copy_buffer);
  636.         }
  637.         return;
  638.     }
  639.     if (fsm_p->pdc.fsmi == Lcp) {  /* LCP Options */
  640.         switch ( o_type ) {
  641.         case LCP_MRU:
  642.             put16(cp, value_p->lcp_option.mru);
  643.             break;
  644.  
  645.         case LCP_ACCM:
  646.             put32(cp, value_p->lcp_option.accm);
  647.             break;
  648.  
  649.         case LCP_AUTHENT:
  650.             put16(cp, value_p->lcp_option.authentication);
  651.             break;
  652.  
  653.         case LCP_MAGIC:
  654.             put32(cp, value_p->lcp_option.magic_number);
  655.             break;
  656.  
  657.         case LCP_PFC:
  658.         case LCP_ACFC:
  659.             break;
  660.  
  661.         }
  662.     } else  {        /* IPCP options */
  663.         switch ( o_type ) {
  664.         case IPCP_ADDRESSES:
  665.             cp = put32(cp, value_p->ipcp_option.address);
  666.             put32(cp, value_p->ipcp_option.other);
  667.             break;
  668.  
  669.         case IPCP_ADDRESS:
  670.             put32(cp, value_p->ipcp_option.address);
  671.             break;
  672.  
  673.         case IPCP_COMPRESS:
  674.             cp = put16(cp, value_p->ipcp_option.compression);
  675.             if ( value_p->ipcp_option.compression == PPP_COMPR_PROTOCOL ) {
  676.                 *cp++ = value_p->ipcp_option.slots - 1;
  677.                 *cp++ = value_p->ipcp_option.slot_compress;
  678.             }
  679.             break;
  680.         }
  681.     }
  682. }
  683.  
  684. /* Build a request to send to remote host */
  685. struct bufheader *
  686. makereq(struct fsm_s *fsm_p)
  687. {
  688.     struct bufheader *req_buffer;
  689.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  690.  
  691.     if (fsm_p->pdc.fsmi == Pap) {
  692.         struct pap_s *pap_p = fsm_p->pdv;
  693.         register char *cp;
  694.         int len, userlen, passlen;
  695.  
  696.         if ( pap_p->username[0] == -1
  697.          ||  pap_p->password[0] == -1 ) {
  698.             PPP_DEBUG_CHECKS("\pNULL username or password");
  699.             return nil;
  700.         }
  701.  
  702.         userlen = pap_p->username[0];
  703.         passlen = pap_p->password[0];
  704.  
  705.         len = 2 + userlen + passlen;
  706.         if ((req_buffer = getbuffer()) == nil)
  707.             return nil;
  708.  
  709.         /* Load user id and password for authenticate packet */
  710.         cp = req_buffer->dataptr;
  711.         *cp++ = userlen;
  712.         if (userlen > 0) {
  713.             BlockMove(&pap_p->username[1], cp, (long) userlen);
  714.             cp += userlen;
  715.         }
  716.         *cp++= passlen;
  717.         if ( passlen > 0 )
  718.             BlockMove(&pap_p->password[1], cp, (long) passlen);
  719.                 
  720.         req_buffer->length += len;
  721.         return(req_buffer);
  722.     }
  723.     
  724.     if ( (req_buffer = getbuffer()) != nil)
  725.         makeoptions( fsm_p, req_buffer, &(proto_p->local.work),
  726.                         proto_p->local.work_negotiate );
  727.     return(req_buffer);
  728. }
  729.